<script setup lang="js">
import { onMounted } from 'vue';
import * as PIXI from 'pixi.js';

onMounted(() => {


    const app = new PIXI.Application({ resizeTo: window });

    document.getElementById('pixi-ShaderToyMesh-container').appendChild(app.view);

    // Build geometry.
    const geometry = new PIXI.Geometry()
        .addAttribute(
            'aVertexPosition', // the attribute name
            [
                -100,
                -100, // x, y
                100,
                -100, // x, y
                100,
                100,
                -100,
                100,
            ], // x, y
            2,
        ) // the size of the attribute
        .addAttribute(
            'aUvs', // the attribute name
            [
                0,
                0, // u, v
                1,
                0, // u, v
                1,
                1,
                0,
                1,
            ], // u, v
            2,
        ) // the size of the attribute
        .addIndex([0, 1, 2, 0, 2, 3]);

    const vertexSrc = `
    
        precision mediump float;
    
        attribute vec2 aVertexPosition;
        attribute vec2 aUvs;
    
        uniform mat3 translationMatrix;
        uniform mat3 projectionMatrix;
    
        varying vec2 vUvs;
    
        void main() {
    
            vUvs = aUvs;
            gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    
        }`;

    const fragmentSrc = `
    //Based on this: https://www.shadertoy.com/view/wtlSWX
    precision mediump float;
    
    varying vec2 vUvs;
    
    uniform sampler2D noise;
    uniform float time;
    
    // Distance function. Just calculates the height (z) from x,y plane with really simple length check.
    // Its not exact as there could be shorter distances.
    vec2 dist(vec3 p)
    {
        float id = floor(p.x)+floor(p.y);
        id = mod(id, 2.);
        float h = texture2D(noise, vec2(p.x, p.y)*0.04).r*5.1;
        return vec2(h-p.z,id);
    }
    
    //Light calculation.
    vec3 calclight(vec3 p, vec3 rd)
    {
        vec2 eps = vec2( 0., 0.001);
        vec3 n = normalize( vec3(
        dist(p+eps.yxx).x - dist(p-eps.yxx).x,
        dist(p+eps.xyx).x - dist(p-eps.xyx).x,
        dist(p+eps.xxy).x - dist(p-eps.xxy).x
        ));
    
        vec3 d = vec3( max( 0., dot( -rd ,n)));
    
        return d;
    }
    
    void main()
    {
        vec2 uv = vec2(vUvs.x,1.-vUvs.y);
        uv *=2.;
        uv-=1.;
    
        vec3 cam = vec3(0.,time -2., -3.);
        vec3 target = vec3(sin(time)*0.1, time+cos(time)+2., 0. );
        float fov = 2.2;
        vec3 forward = normalize( target - cam);
        vec3 up = normalize(cross( forward, vec3(0., 1.,0.)));
        vec3 right = normalize( cross( up, forward));
        vec3 raydir = normalize(vec3( uv.x *up + uv.y * right + fov*forward));
    
        //Do the raymarch
        vec3 col = vec3(0.);
        float t = 0.;
        for( int i = 0; i < 100; i++)
        {
        vec3 p = t * raydir + cam;
        vec2 d = dist(p);
        t+=d.x*0.5;//Jump only half of the distance as height function used is not really the best for heightmaps.
        if(d.x < 0.001)
        {
            vec3 bc = d.y < 0.5 ? vec3(1.0, .8, 0.) :
                    vec3(0.8,0.0, 1.0);
            col = vec3( 1.) * calclight(p, raydir) * (1. - t/150.) *bc;
            break;
        }
        if(t > 1000.)
        {
            break;
        }
        }
        gl_FragColor = vec4(col, 1.);
    }`;

    const uniforms = {
        noise: PIXI.Texture.from('https://pixijs.com/assets/perlin.jpg'),
        time: 0,
    };
    // Make sure repeat wrap is used and no mipmapping.

    uniforms.noise.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
    uniforms.noise.baseTexture.mipmap = false;

    // Build the shader and the quad.
    const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, uniforms);
    const quad = new PIXI.Mesh(geometry, shader);

    quad.position.set(400, 300);
    quad.scale.set(2);

    app.stage.addChild(quad);

    // start the animation..
    let time = 0;

    app.ticker.add((delta) => {
        time += 1 / 60;
        quad.shader.uniforms.time = time;
        quad.scale.set(Number(Math.cos(time)) + 2, Number(Math.sin(time * 0.7)) + 2);
    });
})

</script>

<template>
    <div id="pixi-ShaderToyMesh-container"></div>
</template>

<style scoped></style>
